/*
 * stimer.c
 *
 *  Created on: 2021年12月1日
 *      Author: hello
 */

#include <stdio.h>
#include "stimer.h"



////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
///
/// 移植修改区（互斥锁）
///
///

static void stimer_lock(void)
{
	__disable_irq();
}

static void stimer_unlock(void)
{
	__enable_irq();
}




////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
///
/// 软件定时器实现区
///
///


volatile uint32_t _stimer_ticks = 0;  ///< 定时器计数时基

stimer_t* _stimer_list = NULL;        ///< 定时器列表

/**
 * 定时器句柄
 * @param  timer            定时器句柄
 * @param  name             定时器名称
 * @param  timeout_callback 定时器定时到达回调函数
 * @param  arg              传入定时器的参数
 * @param  ticks            定时器超时时间，ticks的单位取决于时基函数stimer_ticks的调用间隔时间，如果stimer_ticks每1ms调用一次，那么ticks单位就为1ms
 * @param  opt              定时器属性，=STIMER_OPT_SINGLE：单次定时器  =STIMER_OPT_REPEAT：周期定时器
 */
void stimer_init(stimer_t* timer, const char* name, void (*timeout_callback)(void* arg), void* arg, uint32_t ticks, stimet_mode_t mode)
{
    timer->arg           = arg;
    timer->mode          = mode;
    timer->name          = name;
    timer->next          = NULL;
    timer->startup_ticks = 0;
    timer->timeout_ticks = ticks;
    timer->callback      = timeout_callback;
}

/**
 * 启动定时器(根据超时时间顺序插入)
 * @param  timer 定时器句柄
 */
void stimer_start(stimer_t* timer)
{
    stimer_t** p = &_stimer_list;
    uint32_t current_ticks = _stimer_ticks;
	stimer_lock();
    while(*p != NULL)
    {
        if(*p == timer)  goto __exit;
        if((current_ticks + timer->timeout_ticks) < ((*p)->startup_ticks + (*p)->timeout_ticks))  // 当前定时器超时时间小于列表中定时器的超时时间,则将新定时器插在当前定时器前面
            break;
        p = &(*p)->next;
    }
    timer->startup_ticks = current_ticks;
    timer->next = *p;
    *p = timer;
__exit:
	stimer_unlock();
}

#if 0

/**
 * 启动定时器(头部插入)
 * @param  timer 定时器句柄
 */
void stimer_start(stimer_t* timer)
{
    stimer_t* p = _stimer_list;
	stimer_lock();
    while(p != NULL)   // 遍历链表检查当前定时器是否已经存在
    {
        if(p == timer)  goto __exit;
        p = p->next;
    }
    timer->startup_ticks = _stimer_ticks;  // 记录当前时间
    timer->next = _stimer_list;            // 当前定时器连接到链表头所执行的定时器
    _stimer_list = timer;                  // 将链表头指向当前定时器
__exit:
	stimer_unlock();
}

/**
 * 启动定时器((尾部插入))
 * @param  timer 定时器句柄
 */
void stimer_start(stimer_t* timer)
{
    stimer_t** p = &_stimer_list;
    stimer_t* entry = NULL;
	stimer_lock();
    while(*p != NULL)  // 遍历链表检查当前定时器是否已经存在并定位到链表的最后
    {
        entry = *p;
        if(entry == timer)  goto __exit;
        p = &entry->next;
    }
    timer->startup_ticks = _stimer_ticks;  // 记录当前时间
    *p = timer;  // 当将前定时器连接到链表尾部
__exit:
	stimer_unlock();
}
#endif

/**
 * 停止定时器
 * @param  timer 定时器句柄
 */
void stimer_stop(stimer_t* timer)
{
    stimer_t** p = &_stimer_list;
    stimer_t* entry = NULL;
	stimer_lock();
    while(*p != NULL)
    {
        entry = *p;
        if(*p == timer)
        {
            *p = entry->next;
            break;
        }
        p = &entry->next;
    }
	stimer_unlock();
}

/**
 * 重启定时器
 * @param  timer 定时器句柄
 */
void stimer_restart(stimer_t* timer)
{
    if(timer != NULL)
    {
        stimer_stop(timer);
		stimer_start(timer);
    }
}

/**
 * 获取当前定时器已定时时间
 * @param  timer 定时器句柄
 * @return       当前定时时间
 */
uint32_t stimer_getticks(const stimer_t* timer)
{
    return (uint32_t)((int)(_stimer_ticks - timer->startup_ticks));
}

/// 定时器轮训函数，放置于main函数的while死循环中
void stimer_poll(void)
{
    uint32_t current_ticks = _stimer_ticks;
    stimer_t** p = &_stimer_list;
    stimer_t* entry = NULL;
	stimer_lock();
    while(*p != NULL)
    {
        entry = *p;
        if(entry->timeout_ticks > ((uint32_t)((int)(current_ticks - entry->startup_ticks))))  // 未超时
            break;
        if(entry->mode == STIMER_MODE_REPEAT)
        {
            p = &entry->next;  // 周期模式则检查下一个定时器
        }
        else
        {
            *p = entry->next;  // 单次模式则将当前定时器删除
        }
        entry->callback(entry->arg);
        entry->startup_ticks = current_ticks;
    }
	stimer_unlock();
}


/// 定时器时基函数
inline void stimer_ticks(void)
{
    _stimer_ticks++;
}

////////////////////////////////////////////////////////////////////////////
///
///                  示例程序
///

#if 0

void Systick_Handler()  /* 假设此函数一毫秒执行一次 */
{
    stimer_ticks();
}

stimer_t UARTTimer;
stimer_t LED0Timer;

void LED0TimerCallback(void* arg)
{
    printf("%s\r\n", __FUNCTION__);
    fflush(stdout);
}

void UARTTimerCallback(void* arg)
{
    printf("%s\r\n", __FUNCTION__);
    fflush(stdout);
}

int main()
{
    int i = 0;

    stimer_init(
        &LED0Timer,                             // 定时器句柄
        "LED0Timer",                            // 定时器名称
        LED0TimerCallback,                      // 定时器溢出回调函数（注意：不可在回调函数内启动或停止定时器）
        &LED0Timer,                             // 参数
        500,                                    // 定时周期
        STIMER_MODE_REPEAT                      // 周期模式
    );
    stimer_start(&LED0Timer);                   // 启动定时器

    stimer_init(
        &UARTTimer,                             // 定时器句柄
        "UARTTimer",                            // 定时器名称
        UARTTimerCallback,                      // 定时器溢出回调函数（注意：不可在回调函数内启动或停止定时器）
        &UARTTimer,                             // 参数
        1000,                                   // 定时周期
        STIMER_MODE_SINGLE                      // 单次模式
    );
    stimer_start(&UARTTimer);                   // 启动定时器


    for(i = 0; i < 1100; i++)  // 模拟时间走过来1001毫秒
        Systick_Handler();

    for(;;)
    {
        stimer_poll();
    }

    return 0;
}

#endif

